
`timescale 1 ns/1 ps

//---------------------------------------------------
//---------------- UDP 发送 模块 --------------------
// 输入：UDP用户数据，地址，长度信息
// 输出：封装好的UDP帧
//---------------------------------------------------

module axi_mac_tx #
(
    parameter LOCAL_MAC_ADDR  = 48'hC400C4010102,
    parameter LOCAL_IP_ADDR   = {8'd192,8'd168,8'd1,8'd102},
    parameter LOCAL_UDP_PORT  = 16'd4444,
    parameter REMOTE_UDP_PORT = 16'd6666

)
(
    input   wire                clk,
    input   wire                rst,

    input   wire [47:00]        s_axis_tdstmac,
    input   wire [31:00]        s_axis_tdstip,
    input   wire [10:00]        s_axis_tlen,
    input   wire                s_axis_tvalid,
    output  wire                s_axis_tready,
    input   wire [07:00]        s_axis_tdata,
    input   wire                s_axis_tlast,

    output  reg  [07:00]        m_axis_tdata,
    output  reg                 m_axis_tvalid,
    input                       m_axis_tready,
    output  reg                 m_axis_tlast
);

//-----------------------|---- 7 ----|-- 1 --|----- 6 ----|---- 6 -----|----- 2 -----|-- 0 : 1500 ---------------------------------------|- 0:46 -|----- 4 -----|
// Ethernet Frame Format |  Preamble |  SFD  |  Dst Addr  |  Src Addr  | Length/Type |     Data ....                                     |  Pad   |     FCS     |
//-----------------------|-----------|-------|------------|------------|-------------|---------------------------------------------------|--------|-------------|
// Ethernet User Data    |                   |<----------------- 60 - 1514 ------------------------------------------------------------->|                      |
// EMAC                  | 0x55.......  0xD5 |                                                                                           |        |     CRC     |
// MAC Header            |                   |<----------------- 14 ---------------->|                                                   |                      |
// UDP                   |           |       |                         |   0800(IP)  |  IP首部(20B) | UDP首部(8字节) | 数据               |                      |
// UDP Header            |                   |<----------------- 42 ------------------------------------------------>|-------------------|                      |
// USER                  |           |       |                                                                        包计数(4字节) + 数据|                      |
//-----------------------|-----------|-------|-------------------------------------------------------------------------------------------|----------------------|
//------------------------------------------------------------------------------------------------------


localparam              MAC_HEAD_SIZE      = 14;                                             // MAC首部(固定)：14
localparam              IP_HEAD_SIZE       = 5*4;                                            // IP首部(可变)：20
localparam              UDP_HEAD_SIZE      = 8;                                              // UDP首部(固定)：8
localparam              HEAD_SIZE          = MAC_HEAD_SIZE + IP_HEAD_SIZE + UDP_HEAD_SIZE;   // UDP帧，总的首部42(14+20+8)

wire [MAC_HEAD_SIZE*8-1 : 0]  mac_header;     // MAC首部 14 Byte
wire [IP_HEAD_SIZE*8-1  : 0]  ip_header;      // IP首部 20 Byte
wire [UDP_HEAD_SIZE*8-1 : 0]  udp_header;     // UDP首部 8 Byte

//------------------------------------------------------------
//----------------- 以太网首部
//------------------------------------------------------------
reg   [47:0]  s_axis_tdstmac_reg    ;
reg   [31:0]  s_axis_tdstip_reg     ;
reg   [15:0]  ipv4_total_len        ;
reg   [15:0]  udp_total_len         ;

reg   [15:0]  identification        ;
wire  [15:0]  ip_checksum           ;
reg   [15:0]  udp_checksum      = 0 ;

//--------------- MAC首部
assign mac_header = { s_axis_tdstmac_reg, // 48 bit   目的MAC地址
                      LOCAL_MAC_ADDR,     // 48 bit   源MAC地址
                      16'h0800            // 16 bit   Length/Type: 0x0800(IP)
                     };

//--------------- IP首部
assign ip_header  = { 4'd4,               //  4 bit   协议版本       4(IPv4)
                      4'd5,               //  4 bit   IP首部长度     5(DWORD)
                      8'h00,              //  8 bit   服务类型 
                      ipv4_total_len,     // 16 bit   总长度
                      identification,     // 16 bit   标识
                      3'd0,               //  3 bit   标志
                      13'd0,              // 13 bit   片位移
                      8'd64,              //  8 bit   生存时间
                      8'h11,              //  8 bit   协议类型       0x11(UDP)
                      ip_checksum,        // 16 bit   IP首部校验和
                      LOCAL_IP_ADDR,      // 32 bit   源IP地址
                      s_axis_tdstip_reg   // 32 bit   目的IP地址
                    };

//--------------- UDP首部
assign udp_header = { LOCAL_UDP_PORT,     // 16 bit   源端口号
                      REMOTE_UDP_PORT,    // 16 bit   目的端口号
                      udp_total_len,      // 16 bit   UDP长度：         首部+数据 字节数
                      udp_checksum        // 16 bit   UDP真+伪首部校验和
                    };

reg   [5:0]   tx_head_cnt;
wire          tx_head_cnt_end;
reg   [7:0]   tx_head; 

//-------------------------------------------------------
//---------------------------- FSM Define
//-------------------------------------------------------

localparam  IDLE  = 0,  // Tx idle
            HEAD  = 1,  // Tx header
            DATA  = 2;  // Tx data
            
reg [DATA:0] cs,ns;

//--------------- Simulation

// synthesis translate_off
reg [63:0] CS_STRING;
always @(*)
  case(1'b1)
    cs[IDLE]      : CS_STRING = "IDLE"  ;
    cs[HEAD]      : CS_STRING = "HEAD"  ;
    cs[DATA]      : CS_STRING = "DATA"  ;
    default       : CS_STRING = "XXXX"  ;
  endcase
// synthesis translate_on

//-------------------------------------------------------
//---------------------------- FSM
//------------------------------------------------------- 
always @(posedge clk)
begin
  if(rst)  
      cs <= 'd1;
  else        
      cs <= ns;
end

always @(*) 
begin
    ns = 'd0;
    case(1'b1)
        cs[IDLE]:
        begin
            if(s_axis_tvalid)                                     
                ns[HEAD] = 1'b1;
            else                                                  
                ns[IDLE] = 1'b1;
        end
        cs[HEAD]:
        begin
            if(m_axis_tready & tx_head_cnt_end)                   
                ns[DATA] = 1'b1;
            else                                                  
                ns[HEAD] = 1'b1;
        end
        cs[DATA]:
        begin
            if(m_axis_tvalid & m_axis_tready & s_axis_tlast)      
                ns[IDLE] = 1'b1;   // 发送完一包数据
            else                                                  
                ns[DATA] = 1'b1;
        end
        default:                                                
            ns[IDLE] = 1'b1;
    endcase
end

// 保存回发地址等信息
always @(posedge clk)
begin
    if(cs[IDLE] & ns[HEAD]) 
    begin
        s_axis_tdstmac_reg  <= s_axis_tdstmac   ;
        s_axis_tdstip_reg   <= s_axis_tdstip    ;
        ipv4_total_len      <= IP_HEAD_SIZE + UDP_HEAD_SIZE + s_axis_tlen;
        udp_total_len       <= UDP_HEAD_SIZE + s_axis_tlen;
    end
end

// 计算IP首部校验和
localparam [3:0] CHECKSUM_LEN = (IP_HEAD_SIZE+1)/2;   // 取16位的整数倍

reg  [03:0]   checksum_cnt;
wire          checksum_cnt_end;
wire          checksum_en;
reg  [15:0]   checksum_din;
wire [15:0]   checksum_dout;

always @(posedge clk)
begin
    if(cs[IDLE] & ns[HEAD])                            
        checksum_cnt <= 'd1;
    else if(cs[IDLE] | checksum_en & checksum_cnt_end)  
        checksum_cnt <= 'd0;
    else if(checksum_en)                                
        checksum_cnt <= checksum_cnt + 1'b1;
end

assign checksum_cnt_end = checksum_cnt == CHECKSUM_LEN;

assign checksum_en = |checksum_cnt;

always @(posedge clk)
begin
    case(checksum_cnt)
        8'd0:     checksum_din <= ip_header[159:144];
        8'd1:     checksum_din <= ip_header[143:128];
        8'd2:     checksum_din <= ip_header[127:112];
        8'd3:     checksum_din <= ip_header[111:096];
        8'd4:     checksum_din <= ip_header[095:080];
        8'd5:     checksum_din <= ip_header[079:064];
        8'd6:     checksum_din <= ip_header[063:048];
        8'd7:     checksum_din <= ip_header[047:032];
        8'd8:     checksum_din <= ip_header[031:016];
        8'd9:     checksum_din <= ip_header[015:000];
        default:  checksum_din <= 'd0;
    endcase
end

// 校验和初始值为0
assign ip_checksum = checksum_en? 'd0 : checksum_dout;

checksum #
(
    .DW               ( 16              )
)                                     
checksum_i
(                           
    .clk              ( clk             ),
    .rst              ( cs[IDLE]        ),
    .en               ( checksum_en     ),
    .din              ( checksum_din    ),
    .dout             ( checksum_dout   )
);

//--------------- 包计数
always @(posedge clk)
begin
  if(rst)                      
      identification <= 'h0000;
  else if(!cs[IDLE] & ns[IDLE])  
      identification <= identification + 1'b1;
end

//----------------- tx_head_cnt
always @(posedge clk)
begin
  if(rst)                        
      tx_head_cnt <= 'd0;
  else if(cs[IDLE])                 
      tx_head_cnt <= 'd0;
  else if(cs[HEAD] & m_axis_tready) 
      tx_head_cnt <= tx_head_cnt + 1'b1;
end

assign tx_head_cnt_end = tx_head_cnt == HEAD_SIZE - 1;

assign s_axis_tready = cs[DATA] & s_axis_tvalid & m_axis_tready;

//------------------------------------------------
//--------------- UDP/IP首部
always @(*)
begin
    case(tx_head_cnt)
        // ---------------- MAC首部
        8'd00:    tx_head = mac_header[111:104];
        8'd01:    tx_head = mac_header[103:096];
        8'd02:    tx_head = mac_header[095:088];
        8'd03:    tx_head = mac_header[087:080];
        8'd04:    tx_head = mac_header[079:072];
        8'd05:    tx_head = mac_header[071:064];
        8'd06:    tx_head = mac_header[063:056];
        8'd07:    tx_head = mac_header[055:048];
        8'd08:    tx_head = mac_header[047:040];
        8'd09:    tx_head = mac_header[039:032];
        8'd10:    tx_head = mac_header[031:024];
        8'd11:    tx_head = mac_header[023:016];
        8'd12:    tx_head = mac_header[015:008];
        8'd13:    tx_head = mac_header[007:000];
        // ---------------- IP首部
        8'd14:    tx_head = ip_header[159:152];
        8'd15:    tx_head = ip_header[151:144];
        8'd16:    tx_head = ip_header[143:136];
        8'd17:    tx_head = ip_header[135:128];
        8'd18:    tx_head = ip_header[127:120];
        8'd19:    tx_head = ip_header[119:112];
        8'd20:    tx_head = ip_header[111:104];
        8'd21:    tx_head = ip_header[103:096];
        8'd22:    tx_head = ip_header[095:088];
        8'd23:    tx_head = ip_header[087:080];
        8'd24:    tx_head = ip_header[079:072];
        8'd25:    tx_head = ip_header[071:064];
        8'd26:    tx_head = ip_header[063:056];
        8'd27:    tx_head = ip_header[055:048];
        8'd28:    tx_head = ip_header[047:040];
        8'd29:    tx_head = ip_header[039:032];
        8'd30:    tx_head = ip_header[031:024];
        8'd31:    tx_head = ip_header[023:016];
        8'd32:    tx_head = ip_header[015:008];
        8'd33:    tx_head = ip_header[007:000];
        // ---------------- UDP首部
        8'd34:    tx_head = udp_header[63:56];
        8'd35:    tx_head = udp_header[55:48];
        8'd36:    tx_head = udp_header[47:40];
        8'd37:    tx_head = udp_header[39:32];
        8'd38:    tx_head = udp_header[31:24];
        8'd39:    tx_head = udp_header[23:16];
        8'd40:    tx_head = udp_header[15:08];
        8'd41:    tx_head = udp_header[07:00];

        default:  tx_head = 8'h00;
    endcase
end

//----------------------- m_axis_tvalid
always @(*) 
begin
    m_axis_tvalid = cs[HEAD] | cs[DATA] & s_axis_tvalid;
end

//----------------------- m_axis_tdata
always @(*)
begin
  if(cs[HEAD])  
      m_axis_tdata = tx_head[7:0];
  else          
      m_axis_tdata = s_axis_tdata;
end

//----------------------- m_axis_tlast
always @(*)
begin
  m_axis_tlast = s_axis_tlast;
end

/*
always @(posedge clk)
  if(rst)                    m_axis_tfirst <= 1'b0;
  else if(cs[IDLE] & ns[HEAD]) m_axis_tfirst <= 1'b1;
  else if(m_axis_tready)        m_axis_tfirst <= 1'b0;
*/
endmodule
